iT邦幫忙

2024 iThome 鐵人賽

DAY 24
0
Modern Web

前後端整合,用Spring boot 與React 開發屬於自己的記帳網頁系列 第 25

Day25 React前端開發-使用Charts顯示成果

  • 分享至 

  • xImage
  •  

前言

我們在前經歷過二十來天的訓練,我們已經學習了後端、前端開發以及進階的開發方式,接下來我想要來挑戰繪製圖表,畢竟在記帳裡面,圖表是最重要的功能,這能夠讓我們知道這段時間的花費變化,直觀地找出花得最多與最少的時間,而且一般來說大家都教如何開發,比較少人教大家如何使用插件,所以這也是一個新的嘗試。

安裝react-chartjs-2

首先我們先輸入以下的程式碼安裝

npm install react-chartjs-2 chart.js

我們可以在package-lock.json中查看安裝的情況
https://ithelp.ithome.com.tw/upload/images/20241003/20152864DpsXa3jn5M.png
安裝完成之後,我們就要來開發這個頁面

Chart頁面開發

在Component這個資料夾中新增一個ChartComponent.jsxy,因為要使用插件,所以需要匯入比較多插件的相關功能,整個匯入的插件資訊如下

import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from 'chart.js';
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend
);

這些功能的說明如下
Chart as ChartJS:

  • 這是將 Chart 物件重新命名為 ChartJS,以便在你的程式中使用。這個 Chart 是製作圖表的核心元件。
    CategoryScale:
  • 這個元件用於定義 X 軸(橫軸)的類別型縮放比例,特別是當你的 X 軸上是文字或分類數據(例如「一月、二月、三月」)時使用。
    LinearScale:
  • 這個元件用於定義 Y 軸(縱軸)的線性縮放比例。它適用於數值型數據(例如圖表中的數量或數值),將數據在圖表上按比例呈現。
    BarElement:
  • 這個是用來繪製長條圖(Bar Chart)的主要元素,它負責在圖表中繪製一個個的長條。
    Title:
  • 這個元件用來顯示圖表的標題。它會在圖表的上方顯示一個文字作為標題。
    Tooltip:
  • 這個元件負責在使用者將滑鼠懸停在圖表上的某個數據點時,顯示額外的資訊(例如具體的數值或類別名稱)。
    Legend:
  • 這個元件用來顯示圖表中的圖例。當你的圖表包含多組數據時,圖例能夠幫助使用者區分每組數據的顏色或樣式。
    完整的程式碼如下
    這邊整體的邏輯是要藉由輸入的年份、週數,以及取得的Account資料來輸入到Chart中進行繪圖。我們用一週的資料來呈現,所以圖表上必須要有週一到週五,這個我們就要用Label的方式來呈現,最後把資料用charData來輸出
import React, { useEffect, useState } from 'react';
import { Bar } from 'react-chartjs-2';
import { getAccountsByWeek } from '../Service/AccountService';
import {
  Chart as ChartJS,
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend,
} from 'chart.js';
ChartJS.register(
  CategoryScale,
  LinearScale,
  BarElement,
  Title,
  Tooltip,
  Legend
);
const ChartComponent = () => {
    const [year, setYear] = useState('');
    const [week, setWeek] = useState('');
    const [chartData, setChartData] = useState({
        labels: [],
        datasets: [{
            label: '',
            data: [],
            backgroundColor: [],
            borderColor: [],
            borderWidth: 1
        }]
    });
    useEffect(() => {
        // 取得當前年份和週數
        const currentDate = new Date();
        const currentYear = currentDate.getFullYear();
        const currentWeek = getWeekNumber(currentDate);
        setYear(currentYear);
        setWeek(currentWeek);
        // 取得該週的帳戶資料並計算每天的金額加總
        getAccountsByWeek(currentYear, currentWeek).then((response) => {
            const dailyTotals = calculateDailyTotals(response.data);
            setChartData({
                labels: ['Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday', 'Sunday'],
                datasets: [
                    {
                        label: 'Daily Expenses',
                        data: dailyTotals,
                        backgroundColor: 'rgba(75, 192, 192, 0.6)',
                        borderColor: 'rgba(75, 192, 192, 1)',
                        borderWidth: 1,
                    },
                ],
            });
        }).catch((error) => {
            console.error(error);
        });
    }, []);
    // 計算每天的金額加總
    const calculateDailyTotals = (accounts) => {
        const totals = Array(7).fill(0); // 初始化每週 7 天的金額加總為 0
        accounts.forEach((account) => {
            const dayOfWeek = new Date(account.createDate).getDay(); // 取得當前日期是星期幾 (0 是 Sunday)
            const index = dayOfWeek === 0 ? 6 : dayOfWeek - 1; // 將 Sunday 對應到 index 6
            totals[index] += account.amount;
        });
        return totals;
    };
    // Helper function to get the week number
    const getWeekNumber = (date) => {
        const firstDayOfYear = new Date(date.getFullYear(), 0, 1);
        const pastDaysOfYear = (date - firstDayOfYear) / 86400000;
        return Math.ceil((pastDaysOfYear + firstDayOfYear.getDay() + 1) / 7);
    };
    return (
        <div className='container'>
            <h2 className='text-center'>Weekly Expense Chart</h2>
            <div className='d-flex justify-content-center'>
                <div className='chart-container' style={{ height: '400px', width: '600px' }}>
                    <Bar data={chartData} options={{ maintainAspectRatio: false }} />
                </div>
            </div>
        </div>
    );
};
export default ChartComponent;

App.jsx頁面調整

接下來我們回到App.jsx,來新增這個轉址。

import { useState } from 'react'
import reactLogo from './assets/react.svg'
import viteLogo from '/vite.svg'
import './App.css'
import HelloWorld from './Helloworld'
import HeaderComponent from './Component/HeaderComponent'
import FooterComponent from './Component/FooterComponent'
import AccountComponent from './Component/AccountComponent'
import ListAccountComponent from './Component/ListAccountComponent'
import { BrowserRouter, Routes,Route} from 'react-router-dom'
import SortAccountComponent from './Component/SortAccountComponent'
import WeeklyAccountComponent from './Component/WeeklyAccountComponent'
import ChartComponent from './Component/ChartComponent'
function App() {
  
  return (
    <>
    <BrowserRouter>
        <HeaderComponent />
        <Routes>
        {/* http://localhost:8080/ */}
        <Route path='/' element={<ListAccountComponent />}></Route>
          {/* http://localhost:8080/chart-account */}
          <Route path='/chart-account' element={<ChartComponent />}></Route>
          {/* http://localhost:8080/weekly-account' */}
          <Route path='/week-account' element={<WeeklyAccountComponent />}></Route>
          {/* http://localhost:8080/list-account */}
          <Route path='/lsit-account' element={<ListAccountComponent />}></Route>
          {/* http://localhost:8080/add-account */}
          <Route path='/add-account' element={<AccountComponent />}></Route>
          {/* http://localhost:8080/add-account/1 */}
          <Route path='/update-account/:id' element={<AccountComponent />}></Route>
          {/* http://localhost:8080/sort-account/ */}
          <Route path='/sort-account' element={<SortAccountComponent />}></Route>
        </Routes>
        <FooterComponent />
    </BrowserRouter>
    </>
  )
}
export default App

Header頁面調整

新增完成之後,我們來到Header頁面新增這個連結

/* eslint-disable no-unused-vars */
import React, { useState } from 'react'
import { NavLink } from 'react-router-dom'
const HeaderComponent = () => {
  return (
    <div>
        <header>
            <nav className='navbar navbar-expand-md navbar-dark bg-dark'>
            <div>
                    <a href='http://localhost:5173/' className='navbar-brand'>
                    Account Manager Application</a>
            </div>
            <div className='collapse navbar-collapse'>
            <ul className='navbar-nav'>
                <li className='nav-item'>
                <NavLink to="/lsit-account" className="nav-link">Account List</NavLink>
                </li>
                <li className='nav-item'>
                <NavLink to="/sort-account" className="nav-link">Sort Account</NavLink>
                </li>
                <li className='nav-item'>
                <NavLink to="/week-account" className="nav-link">Week Account</NavLink>
                </li>
                <li className='nav-item'>
                <NavLink to="/chart-account" className="nav-link">Chart Account</NavLink>
                </li>
              </ul>
            </div>
            </nav>
        </header>
    </div>
  )
}
export default HeaderComponent

結果如下
https://ithelp.ithome.com.tw/upload/images/20241003/20152864JUa8VKWk8d.png
到了這裡,我們就成功的把進階的資料,用我們引入的插件,用圖表的方式呈現出我們的資料了!
到這裡,相信大家已經熟練地完成後端、前端,以及前後端整合的練習了,但網頁開發除了這些需求開發之外,還有安全性的開發與設置,在這裏,我們將使用Spring boot Security來為我們的網站增加安全性
那各位夥伴,我們就明天見囉!


上一篇
Day24 React前端開發 : 根據指定週數顯示帳目的網頁
下一篇
Day26 Java Srping boot 後端安全管理:Spring boot Security介紹
系列文
前後端整合,用Spring boot 與React 開發屬於自己的記帳網頁30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言